#include "FKApplicationManager.h"
#include "../FKTools/FKPostOperations.h"
#include <QProcess>
#include <QDateTime>
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <iostream>

FKApplicationManager::FKApplicationManager(int argc, char **argv, QString app) :
        QCoreApplication(argc,argv),_app(app),_commands(),_restart(false){
    _process=new QProcess(this);
    QProcessEnvironment env(QProcessEnvironment::systemEnvironment());
#ifdef Q_OS_MAC
    QString appDir=applicationDirPath() + QString("/bin_mac");
    const QString variable("DYLD_LIBRARY_PATH");
    const QString separator(":");
    const QString extention("");
#elif defined(Q_OS_WIN)
    QString appDir=applicationDirPath().replace("/","\\") + QString("\\bin_win");
    const QString variable("PATH");
    const QString separator(";");
    const QString extention(".exe");
#else
    QString appDir=applicationDirPath() + QString("/bin_nix");
    const QString variable("LD_LIBRARY_PATH");
    const QString separator(":");
    const QString extention("");
#endif
    env.insert(variable,env.value(variable)+separator+appDir);
    _process->setProcessEnvironment(env);
    _process->setInputChannelMode(QProcess::ForwardedInputChannel);
    _app=appDir.replace("\\","/")+QString("/")+_app+extention;
    connect(_process,SIGNAL(readyReadStandardOutput()),SLOT(getCommand()));
    connect(_process,SIGNAL(finished(int)),SLOT(processCommands(int)));
    _avaliableCommands<<FKPostOperations::restartApplication<<FKPostOperations::backUpAndReplaceFiles;
    start();
}

FKApplicationManager::~FKApplicationManager(){
    if(_process->state()!=QProcess::NotRunning)_process->terminate();
}

void FKApplicationManager::start(){
    std::cout<<"Starting "<<_app.toStdString()<<"\n";
    _process->start(_app,QStringList());
    std::cout<<"running...\n";
}

void FKApplicationManager::getCommand(){
    _cache+=_process->readAllStandardOutput();
    int i=_cache.indexOf('@');
    while(i>=0){
        if(i>0){
            std::cout<<_cache.left(i).toStdString();
            _cache.remove(0,i);
        }
        if(_cache.size()<6)return;
        qint32 msize=_cache.mid(1,4).toInt();
        if(_cache.size()<msize+5)return;
        QString mes(_cache.mid(5,msize));
        if(_avaliableCommands.contains(mes.split(';').first())){
            std::cout<<mes.toStdString()<<" requested\n";
            _commands.append(mes);
        }else{
            std::cout<<mes.toStdString()<<"\n";
        }
        _cache.remove(0,msize+5);
        i=_cache.indexOf('@');
    }
    std::cout<<_cache.toStdString();
    _cache.clear();
}

void FKApplicationManager::processCommands(int exitCode){
    if(exitCode){
        std::cout<<"Application exit with error. Press \"enter\" to close.\n";
        getchar();
        quit();
    }else{
        int i=1;
        foreach(QString command, _commands){
            std::cout<<"Proccessing command "<< i++ <<" of "<<_commands.size()<<": "<<command.toStdString()<<"\n";
            processCommand(command);
        }
        _commands.clear();
        if(_restart){
            _restart=false;
            start();
        }else{
            std::cout<<"Process finished\n";
            quit();
        }
    }
}

void FKApplicationManager::processCommand(QString command){
    QStringList args(command.split(';'));
    QString operation(args.takeFirst());
    if(operation==FKPostOperations::backUpAndReplaceFiles){
        QString backUpPath(QString("backup/")+QDateTime::currentDateTime().toString(QString("dd-MM-yy_hh-mm-ss"))+QString("/bin"));
        while(args.size()>=2){
            QString from(args.takeFirst());
            QString to(args.takeFirst());
            backUpAndCopy(from,to,backUpPath);
        }
        if(args.size()>0)error(QString("Excess argument left: ")+args.first());
    }else if(operation==FKPostOperations::restartApplication){
        _restart=true;
    }
}

bool FKApplicationManager::backUpAndCopy(QString initialFile, QString targetDirectory, QString backUpDirectory){
    std::cout<<"\nCopy file "<<initialFile.toStdString()<<" to location "<<targetDirectory.toStdString()<<"\n";
    QFileInfo target(QDir(QCoreApplication::applicationDirPath()+QString("/bin")),targetDirectory);
    QFileInfo backup(QDir(QCoreApplication::applicationDirPath()+QString("/")+backUpDirectory),targetDirectory);
    std::cout<<"\n"<<target.absoluteFilePath().toStdString()<<"\n";
    std::cout<<"\n"<<backup.absoluteFilePath().toStdString()<<"\n";
    QFile sourceFile(initialFile);
    if(!sourceFile.exists()){
        error(QString("Cannot find source copy file."));
        return false;
    }
    std::cout<<"Creating directories...\n";
    target.dir().mkpath(".");
    backup.dir().mkpath(".");
    QString fileName=QFileInfo(sourceFile).fileName();
    QString newFilePath=target.absoluteFilePath();
    if(target.dir().entryList().contains(fileName)){
        std::cout<<"Move old file...\n";
        QFile oldFile(newFilePath);
        if(!oldFile.rename(backup.absoluteFilePath())){
            error(QString("Unable move old file."));
            return false;
        }
    }
    std::cout<<"Move new file...\n";
    if(!sourceFile.rename(newFilePath)){
        error(QString("Unable move new file."));
        return false;
    }
    return true;
}

void FKApplicationManager::error(QString description){
    std::cout<<"\n"<<description.toStdString()<<"\nOperation terminated, press \"enter\" to close.\n";
    getchar();
    quit();
}
